package com.goldman.aplt.util;

import com.goldman.aplt.base.constant.ApConstants;
import com.goldman.aplt.base.exception.BankBusiException;
import com.goldman.aplt.base.exception.BusiErrorCode;
import com.goldman.aplt.base.type.BaseEnumType;
import com.goldman.aplt.base.type.CmErrorCode;
import com.goldman.aplt.base.type.ComAp;
import com.goldman.aplt.base.type.TrxnEnvs;
import com.goldman.base.common.sys.exception.BusinessException;
import com.goldman.http.common.s2s.S2SOption;
import com.goldman.http.common.s2s.model.ErrorCode;
import com.goldman.http.common.s2s.model.HeaderConstants;
import com.goldman.oss.common.FileStorageProcessorFactory;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.joda.time.Months;
import org.joda.time.Years;
import org.springframework.beans.factory.annotation.Value;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;

import static com.goldman.aplt.base.constant.ApConstants.ENUM_DESC_ATTR_NAME;
import static com.goldman.aplt.base.constant.ApConstants.ENUM_SYMBOL;

@Slf4j
public class CommUtil {

    @Value("${systemId}")
    private static String systemId;
    public static final String CommUtil_01 = "类型[%s]不是Map类型不能转换";
    public static final String CommUtil_02 = "进行lpad的添加字符串不能为null且长度不能为0!";
    public static final String CommUtil_03 = "进行lpad的长度无效!";
    public static final String CommUtil_04 = "无效的编码值[%s]";
    public static final String CommUtil_05 = "进行Rpad的添加字符串不能为null且长度不能为0!";
    public static final String CommUtil_06 = "进行Rpad的长度无效!";
    public static final String CommUtil_07 = "like函数参数值无效!";
    public static final String CommUtil_08 = "like函数不支持中间匹配!";
    public static final String CommUtil_09 = "In函数参数值无效!";
    public static final String CommUtil_10 = "In函数参数类型必须相同!";
    public static final String CommUtil_11 = "Between函数参数值无效!";
    public static final String CommUtil_12 = "Between函数参数类型必须相同!";
    public static final String CommUtil_13 = "round精度不能为负数!";
    public static final String CommUtil_14 = "Floor参数不能为null!";
    public static final String CommUtil_15 = "Ceil参数不能为null!";
    public static final String CommUtil_16 = "toMap转换异常!";
    public static final String CommUtil_17 = "卡号/账号格式不符合规则!";

    public static Map<String, Object> toMap(Object bean) {
        if (bean == null) {
            return null;
        } else if (bean instanceof Map) {
            return (Map) bean;
        } else {
            throw new IllegalArgumentException(CommUtil_16);
        }
    }

    public static List<Map<String, Object>> toListMap(List<?> list) {
        if (list == null) {
            return null;
        } else {
            List<Map<String, Object>> ret = new ArrayList();
            Iterator var2 = list.iterator();

            while (var2.hasNext()) {
                Object o = var2.next();
                ret.add(toMap(o));
            }

            return ret;
        }
    }

//    public static void copyProperties(Object dest, Object src) {
//        copyProperties(src, dest, true);
//    }
//
//    public static void copyPropertiesWithTypeConvert(Object dest, Object src) {
//        copyPropertiesWithTypeConvert(dest, src, true);
//    }
//
//    public static void copyPropertiesWithTypeConvert(Object dest, Object src, boolean withNoEmpty) {
//        BaseBeanUtil.copyProperties(src, dest, withNoEmpty, (List) null, true);
//    }
//
//    public static void copyProperties(Object src, Object dest, boolean withNoEmpty) {
//        BaseBeanUtil.copyProperties(src, dest, withNoEmpty, (List) null, false);
//    }
//
//    public static void copyElement(Object src, Object dest, boolean withNoEmpty) {
//        BaseBeanUtil.copyProperties(src, dest, withNoEmpty, (List) null, false);
//    }
//
//    public static void copyPropertyList(Object dest, Object src, String... propList) {
//        BaseBeanUtil.copyProperties(src, dest, true, Arrays.asList(propList), false);
//    }

    public static <T> T nvl(T s, T defaultValue) {
        return isNull(s) ? defaultValue : s;
    }

    public static boolean isNull(Object o) {
        if (isEmptyOrBlank(o, false)) {
            return true;
        }
        return false;
    }

    private static boolean isEmptyOrBlank(Object obj, boolean trim) {
        if (obj == null) {
            return true;
        } else if (obj instanceof String) {
            String ss = (String) obj;
            return (trim ? ss.trim() : ss).length() == 0;
        } else if (obj instanceof Object[]) {
            return isEmptyOrBlankObj(obj, trim);
        } else if (obj instanceof Collection) {
            return isEmptyOrBlankCollection(obj, trim);
        } else {
            return obj instanceof Map ? ((Map) obj).isEmpty() : false;
        }
    }

    private static boolean isEmptyOrBlankCollection(Object obj, boolean trim) {
        Collection<Object> oo = (Collection) obj;
        Iterator i = oo.iterator();

        do {
            if (!i.hasNext()) {
                return true;
            }
        } while (isEmptyOrBlank(i.next(), trim));

        return false;
    }

    private static boolean isEmptyOrBlankObj(Object obj, boolean trim) {
        Object[] oo = (Object[]) ((Object[]) obj);

        for (int i = 0; i < oo.length; ++i) {
            if (!isEmptyOrBlank(oo[i], trim)) {
                return false;
            }
        }

        return true;
    }

    public static boolean isNotNull(Object o) {
        return !isNull(o);
    }

    public static void amtNotNegative(BigDecimal amt) {
        if (amt.compareTo(BigDecimal.ZERO) <= 0) {
//            throw new BusinessException(BusiErrorCode.FAILED, "Input amount must be greater than 0!");
            throw new BankBusiException(CmErrorCode.TRXN_AMT_ERROR);
        }
    }

    public static void limitTextLength(String remark, int maxLength) {
        if (CommUtil.isNotNull(remark) && remark.length() > maxLength) {
//            throw  new BusinessException(BusiErrorCode.FAILED, "Input text too long!");
            throw new BankBusiException(CmErrorCode.FIELD_NOT_LEGAL, new String[]{"remark"});
        }
    }

    public static void limitRecodeSize(int recordSize, int maxSize) {
        if (recordSize > maxSize) {
            throw new BusinessException(BusiErrorCode.FAILED, "Maximum support for " + maxSize + " items!");
        }
    }

    public static String trim(String s) {
        return s == null ? null : s.trim();
    }

    public static String rtrim(String s) {
        if (s == null) {
            return null;
        } else {
            StringBuffer sb = new StringBuffer(s);

            for (int i = sb.length() - 1; i >= 0 && sb.charAt(i) == ' '; --i) {
                sb.deleteCharAt(i);
            }

            return sb.toString();
        }
    }

    public static String ltrim(String s) {
        if (s == null) {
            return null;
        } else {
            String ret = "";

            for (int i = 0; i < s.length(); ++i) {
                if (s.charAt(i) != ' ') {
                    ret = s.substring(i);
                    break;
                }
            }

            return ret;
        }
    }

    public static String lpad(String s, int i, String s1) {
        if (s == null) {
            return null;
        } else if (s1 != null && s1.length() > 0) {
            if (i <= 0) {
                throw new IllegalArgumentException(CommUtil_03);
            } else if (i <= s.length()) {
                return s.substring(0, i);
            } else {
                StringBuffer sb = new StringBuffer(s);
                char[] c1 = s1.toCharArray();
                boolean var5 = false;

                while (sb.length() < i) {
                    int index = 0;

                    while (sb.length() < i && index < c1.length) {
                        sb.insert(0, c1[index++]);
                    }
                }

                return sb.toString();
            }
        } else {
            throw new IllegalArgumentException(CommUtil_02);
        }
    }

    public static String lpad(String s, int i, String s1, String enCoding) {
        if (s == null) {
            return null;
        } else if (s1 != null && s1.length() > 0) {
            if (i <= 0) {
                throw new IllegalArgumentException(CommUtil_03);
            } else {
                try {
                    byte[] pad = s1.getBytes(enCoding);
                    byte[] by = s.getBytes(enCoding);
                    byte[] des = new byte[i];
                    int srcLen = by.length;
                    int padLen = pad.length;
                    if (i <= srcLen) {
                        System.arraycopy(by, 0, des, 0, i);
                        return new String(des, enCoding);
                    } else {
                        System.arraycopy(by, 0, des, i - srcLen, srcLen);

                        for (; srcLen < i; srcLen += padLen) {
                            if (padLen + srcLen > i) {
                                System.arraycopy(pad, 0, des, 0, i - srcLen);
                            } else {
                                System.arraycopy(pad, 0, des, i - srcLen - padLen, padLen);
                            }
                        }

                        return new String(des, enCoding);
                    }
                } catch (UnsupportedEncodingException var9) {
                    throw new IllegalArgumentException(String.format(CommUtil_04, enCoding));
                }
            }
        } else {
            throw new IllegalArgumentException(CommUtil_02);
        }
    }

    public static String rpad(String s, int i, String s1) {
        if (s == null) {
            return null;
        } else if (s1 != null && s1.length() > 0) {
            if (i <= 0) {
                throw new IllegalArgumentException(CommUtil_06);
            } else if (i <= s.length()) {
                return s.substring(0, i);
            } else {
                StringBuffer sb = new StringBuffer(s);
                char[] c1 = s1.toCharArray();
                boolean var5 = false;

                while (sb.length() < i) {
                    int index = 0;

                    while (sb.length() < i && index < c1.length) {
                        sb.append(c1[index++]);
                    }
                }

                return sb.toString();
            }
        } else {
            throw new IllegalArgumentException(CommUtil_05);
        }
    }

    public static String rpad(String s, int i, String s1, String enCoding) {
        if (s == null) {
            return null;
        } else if (s1 != null && s1.length() > 0) {
            if (i <= 0) {
                throw new IllegalArgumentException(CommUtil_06);
            } else {
                try {
                    byte[] pad = s1.getBytes(enCoding);
                    byte[] by = s.getBytes(enCoding);
                    byte[] des = new byte[i];
                    int srcLen = by.length;
                    int padLen = pad.length;
                    if (i <= by.length) {
                        System.arraycopy(by, 0, des, 0, i);
                        return new String(des, enCoding);
                    } else {
                        System.arraycopy(by, 0, des, 0, srcLen);

                        for (; srcLen < i; srcLen += padLen) {
                            if (padLen + srcLen > i) {
                                System.arraycopy(pad, 0, des, srcLen, i - srcLen);
                            } else {
                                System.arraycopy(pad, 0, des, srcLen, padLen);
                            }
                        }

                        return new String(des, enCoding);
                    }
                } catch (UnsupportedEncodingException var9) {
                    throw new IllegalArgumentException(String.format(CommUtil_04, enCoding));
                }
            }
        } else {
            throw new IllegalArgumentException(CommUtil_05);
        }
    }

    public static boolean like(String s1, String s2) {
        if (s1 != null && s2 != null) {
            int len = s2.length();
            boolean startWith = false;
            boolean endWith = false;
            if (s2.charAt(0) == '%') {
                startWith = true;
            }

            int p = s2.indexOf(37, 1);
            if (p > 0 && p < s2.length() - 1) {
                throw new IllegalArgumentException(CommUtil_08);
            } else {
                if (s2.charAt(len - 1) == '%') {
                    endWith = true;
                }

                s2 = s2.replace("%", "");
                if (startWith && endWith) {
                    return s1.indexOf(s2) >= 0;
                } else if (startWith) {
                    return s1.endsWith(s2);
                } else {
                    return endWith ? s1.startsWith(s2) : s1.equals(s2);
                }
            }
        } else {
            throw new IllegalArgumentException(CommUtil_07);
        }
    }

//    public static boolean in(Object a, Object... a1) {
//        if (a == null) {
//            return false;
//        } else if (a1 != null && a1.length > 0) {
//            for (int i = 0; i < a1.length; ++i) {
//                if (!a.getClass().isAssignableFrom(a1[i].getClass())) {
//                    throw new IllegalArgumentException(CommUtil_10);
//                }
//
//                if (compare(a, a1[i]) == 0) {
//                    return true;
//                }
//            }
//
//            return false;
//        } else {
//            throw new IllegalArgumentException(CommUtil_09);
//        }
//    }

    public static <T> boolean in(T object, List<T> objects) {
        return objects.indexOf(object) >= 0;
    }

//
//    public static boolean Between(Object a, Object start, Object end) {
//        if (a != null && start != null && end != null) {
//            if (a.getClass().isAssignableFrom(start.getClass()) && a.getClass().isAssignableFrom(end.getClass())) {
//                return compare(a, start) >= 0 && compare(a, end) <= 0;
//            } else {
//                throw new IllegalArgumentException(CommUtil_12);
//            }
//        } else {
//            throw new IllegalArgumentException(CommUtil_11);
//        }
//    }

//    public static <T extends Comparable<? super T>> int compare(T o1, T o2, boolean ignoreCase,
//                                                                boolean ignoreNullAndEmpty) {
//        return ComparableUtil.compare(o1, o2, ignoreCase, ignoreNullAndEmpty);
//    }
//
//    public static int compare(Object o1, Object o2) {
//        return ComparableUtil.compare(o1, o2);
//    }
//
//    public static BigDecimal trunc(BigDecimal am) {
//        return am == null ? null : ConvertUtil.toBigDecimal(am, (BigDecimal) null);
//    }

    public static BigDecimal round(BigDecimal amt, int scale) {
        return round(amt, scale, 4);
    }

    public static BigDecimal round(BigDecimal amt, int scale, int roundingMode) {
        if (amt == null) {
            return null;
        } else if (scale < 0) {
            throw new IllegalArgumentException(CommUtil_13);
        } else {
            return amt.setScale(scale, roundingMode);
        }
    }

    public static long floor(Object val) {
        if (val == null) {
            throw new IllegalArgumentException(CommUtil_14);
        } else if (val instanceof BigDecimal) {
            return ((BigDecimal) val).longValue();
        } else if (val instanceof Double) {
            return (long) Math.floor((Double) val);
        } else if (val instanceof Float) {
            return (long) Math.floor((double) (Float) val);
        } else {
            return val instanceof Long ? (Long) val : 0L;
        }
    }

    public static long ceil(Object val) {
        if (val == null) {
            throw new IllegalArgumentException(CommUtil_15);
        } else if (val instanceof BigDecimal) {
            return round((BigDecimal) val, 0, 2).longValue();
        } else if (val instanceof Double) {
            return (long) Math.ceil((Double) val);
        } else {
            return val instanceof Float ? (long) Math.ceil((double) (Float) val) : 0L;
        }
    }

//    public static boolean equals(Object o1, Object o2) {
//        return compare(o1, o2) == 0;
//    }

    public static Map<String, Object> shrinkHttpParameters(Map<String, String[]> parameters) {
        boolean decode = parameters.containsKey("q:");
        Map<String, Object> ret = new HashMap();
        Iterator var3 = parameters.entrySet().iterator();

        while (true) {
            while (var3.hasNext()) {
                Entry<String, String[]> entry = (Entry) var3.next();
                String[] value = (String[]) entry.getValue();
                if (value.length == 1) {
                    ret.put(entry.getKey(), convertHtmlParameter(value[0], decode));
                } else {
                    List<String> values = new ArrayList();
                    String[] var8 = value;
                    int var9 = value.length;

                    for (int var10 = 0; var10 < var9; ++var10) {
                        String v = var8[var10];
                        if (isNotNull(v)) {
                            values.add(convertHtmlParameter(v, decode));
                        }
                    }

                    ret.put(entry.getKey(), values.toArray(new String[values.size()]));
                }
            }

            return ret;
        }
    }

    private static String convertHtmlParameter(String p, boolean decode) {
        if (decode) {
            try {
                p = new String(p.getBytes("ISO8859-1"), "UTF-8");
            } catch (UnsupportedEncodingException var3) {
                throw new RuntimeException(var3);
            }
        }

        if (isNotNull(p)) {
            p = p.replaceAll("(?im)<([\\s/]*(script|iframe|img))", "&lt;$1");
        }

        return p;
    }

    /**
     * @param cardno 不含校验位的账号(卡号)
     * @return 校验位
     * @Author chensy
     * <p>
     * <li>2016年12月13日-上午9:57:21</li>
     * <li>功能说明：生成账\卡号校验位，模10隔位乘2加"校验位算法。</li>
     * </p>
     */
    public static int genCardnoCheckBit(String cardno) {
//        fieldNotNull(cardno, "acct_no", "account no");
        cardno = CommUtil.trim(cardno);

        if (!cardno.matches("^[0-9]*$")) {
            throw new IllegalArgumentException(CommUtil_17);
        }

        int sum = 0;
        int temp = 0;
        char[] array = cardno.toCharArray();
        for (int i = 0; i < array.length; i++) {
            if (i % 2 == 0) {
                temp = (Character.getNumericValue(array[i]) * 2);
                if (temp > 9) {
                    temp = temp - 9;
                }
            } else {
                temp = Character.getNumericValue(array[i]);
            }
            sum += temp;
        }

        int parity = 10 - sum % 10;
        if (parity == 10) {
            parity = 0;
        }

        return parity;
    }

    public static TrxnEnvs getTrxnEnvs() {
        return TrxnContext.getInstance().getTrxnEnvs();
    }

    public static void setTrxnEnvs(TrxnEnvs envs) {
        TrxnContext.getInstance().setTrxnEnvs(envs);
    }

    /**
     * 获取当前机器时间，字符串格式（yyyyMMdd HH:mm:ss SSS）
     */
    public static String getComputerDateTime() {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd HH:mm:ss SSS");
        return df.format(new Date());
    }

    public static String getComputerDateTime(String formatStr) {
        SimpleDateFormat df = new SimpleDateFormat(formatStr);
        return df.format(new Date());
    }

    public static Date parseStringToDate(String datetimeString) {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd HH:mm:ss SSS");
        try {
            return df.parse(datetimeString);
        } catch (ParseException e) {
            throw new BusinessException(BusiErrorCode.FAILED, "parse time failure.");
        }
    }

    /**
     * 获取当前机器日期，格式：yyyyMMdd
     */
    public static String getComputerDate() {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
        return df.format(new Date());
    }

    /**
     * 获取当前机器时间，格式：HHmmss
     */
    public static String getComputerSimpleTime() {
        SimpleDateFormat df = new SimpleDateFormat("HHmmss");
        return df.format(new Date());
    }

    /**
     * 获取当前机器时间，格式：HH:mm:ss SSS
     */
    public static String getComputerTime() {
        SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss SSS");
        return df.format(new Date());
    }

    /**
     * 将日期字符串格式为日期对象，格式：yyyyMMdd
     */
    public static Date toDate(String date) {
        return toDate(date, "yyyyMMdd");

    }

    /**
     * 将日期字符串格式为日期对象
     *
     * @param date
     * @param format
     * @return
     */
    public static Date toDate(String date, String format) {
        SimpleDateFormat df = new SimpleDateFormat(format);
        Date d = new Date();
        try {
            d = df.parse(date);
        } catch (ParseException e) {
            throw new BusinessException(ErrorCode.SYS_ERROR, e.getMessage(), e);
        }
        return d;
    }

    /**
     * 将日期对象格式化为日期字符串，格式：yyyyMMdd
     */
    public static String formatDate(Date date) {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
        return df.format(date);
    }

    public static String formatDate(Date date, String format) {
        SimpleDateFormat df = new SimpleDateFormat(format);
        return df.format(date);
    }

    /**
     * 只接受yyyyMMdd
     *
     * @param dateStr
     * @return
     */
    public static String formatDateToApp(String dateStr) {
        if (CommUtil.isNull(dateStr) || dateStr.length() != 8) {
            return "";
        }

        return CommUtil.formatDate(CommUtil.toDate(dateStr, "yyyyMMdd"), "dd-MM-yyyy");
    }

    /**
     * @param dateStr
     * @param outputFormat MMM dd, yyyy
     * @return
     */
    public static String formatDate(String dateStr, String outputFormat) {
        if (CommUtil.isNull(dateStr)) {
            return "";
        }

        int ind = -1;
        if ((ind = dateStr.indexOf(".")) > -1) {
            dateStr = dateStr.substring(0, ind);
        }

        String intputFormat = "";
        if (dateStr.length() == 8) {
            intputFormat = "yyyyMMdd";
        } else if (dateStr.length() == 10) {
            intputFormat = "yyyy-MM-dd";
        } else if (dateStr.length() == 17) {
            intputFormat = "yyyyMMdd HH:mm:ss";
        } else if (dateStr.length() == 19) {
            intputFormat = "yyyy-MM-dd HH:mm:ss";
        } else if (dateStr.length() == 21) {
            intputFormat = "yyyyMMdd HH:mm:ss SSS";
        } else if (dateStr.length() == 23) {
            intputFormat = "yyyy-MM-dd HH:mm:ss SSS";
        }

        Date date = toDate(dateStr, intputFormat);

        SimpleDateFormat df = new SimpleDateFormat(outputFormat, Locale.ENGLISH);
        return df.format(date);
    }


    /**
     * 将日期对象格式化为日期字符串，格式：yyyyMMdd HH:mm:ss SSS
     */
    public static String formatDateTime(Date date) {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd HH:mm:ss SSS");
        return df.format(date);
    }

    /**
     * 检查是否是yyyyMMdd格式的日期字符串
     */
    public static boolean isDateString(String date) {
        return isDateString(date, "yyyyMMdd");

    }

    /**
     * @param date
     * @param format
     * @return
     * @Author lid
     * <p>
     * <li>2017年3月16日-上午10:11:48</li>
     * <li>功能说明：检查是否是格式的日期字符串</li>
     * </p>
     */
    public static boolean isDateString(String date, String format) {
        if (CommUtil.isNull(date))
            return false;

        SimpleDateFormat df = new SimpleDateFormat(format);

        try {
            String tmp = df.format(toDate(date, format));
            if (date.equals(tmp)) {
                return true;
            }
        } catch (Exception e) {
            log.error("date format error:" + e.getMessage(), e, new Object[0]);
        }
        return false;
    }

    /**
     * @param cycle 周期 ex: 7D 1Y 30D
     * @return true 合法 false 不合法
     * @Author T
     * <p>
     * <li>2014年3月4日-下午3:00:28</li>
     * <li>功能说明：判断周期字符串是否合法</li>
     * </p>
     */
    public static boolean isCycleString(String cycle) {

        if (CommUtil.isNull(cycle)) {
            return false;
        }

        boolean ret = false;
        int cycleLen = cycle.length();
        // 最少两位
        if (cycleLen >= 2) {
            // 周期单位
            String cycleUnit = cycle.substring(cycleLen - 1);
            // 最后一位必须在周期单位准许范围之内
            if (BaseEnumType.E_CYCLETYPE.isIn(cycleUnit)) {
                // 前面应该是数字部分如果转换为整数报错，则表明不是数值型字符串
                try {
                    // 周期数字
                    String cycleValue = cycle.substring(0, cycleLen - 1);
                    Integer.parseInt(cycleValue);
                    ret = true;
                } catch (Exception e) {
                    log.error("cycle value parse error:" + e.getMessage(), e, new Object[0]);
                }
            }
        }
        return ret;
    }

    public static void fieldNotNull(Object data) {
        if (CommUtil.isNull(data)) {
            throw new BankBusiException(CmErrorCode.ARG_ERROR, new String[]{""});
        }
    }

    public static void fieldNotNull(Object data, String filedName) {
        if (CommUtil.isNull(data)) {
            throw new BankBusiException(CmErrorCode.ARG_ERROR, new String[]{filedName});
        }
    }

    public static void fieldNotNullTip(Object data, String filedName) {
        if (CommUtil.isNull(data)) {
            throw new BankBusiException(CmErrorCode.NULL_ERROR, new String[]{filedName});
        }
    }

    /**
     * @param type day\dd\DD\d\D week\ww\WW\w\W month\mm\MM\m\M quarter\qq\QQ\q\Q
     *             year\yy\YY\y\Y
     * @param date 基准日期
     * @param num  日期间隔
     * @return
     * @Author zw
     * <p>
     * <li>2016年12月9日-上午9:20:49</li>
     * <li>功能说明：根据类型，数量，基准日期计算结果日期</li>
     * </p>
     */
    public static String dateAdd(String type, String date, int num) {

        String newType = type;
        int newNum = num;
        // 将D,Y,M 转换为DD, YY, MM
        if (1 == type.length())
            newType += type;

        if ("HH".equalsIgnoreCase(newType)) {
            newType = "MM";
            newNum *= 6;
        }

        return dateAdd2(newType.toLowerCase(), date, newNum);
    }

    public static String dateAdd2(String type, String date, int num) {
        Date d1 = toDate(date);
        byte precision;
        int amount;
        if (!"day".equals(type) && !"dd".equals(type)) {
            if (!"year".equals(type) && !"yy".equals(type)) {
                if (!"month".equals(type) && !"mm".equals(type)) {
                    if (!"quarter".equals(type) && !"qq".equals(type)) {
                        if (!"week".equals(type) && !"ww".equals(type)) {
                            throw new IllegalArgumentException("Parameter " + type + " is invalid!");
                        }

                        precision = 6;
                        amount = 7 * num;
                    } else {
                        precision = 2;
                        amount = 3 * num;
                    }
                } else {
                    precision = 2;
                    amount = num;
                }
            } else {
                precision = 1;
                amount = num;
            }
        } else {
            precision = 6;
            amount = num;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(d1);
        cal.add(precision, amount);
        return formatDate(cal.getTime());
    }

    /**
     * 生成固定长度的随机数
     *
     * @param length
     * @return
     */
    public static String generateRandomNumber(int length) {
        String characters = "0123456789";
        StringBuilder sb = new StringBuilder(length);
        Random random = new Random();

        for (int i = 0; i < length; i++) {
            int index = random.nextInt(characters.length());
            sb.append(characters.charAt(index));
        }
        return sb.toString();
    }

    /**
     * 把枚举转换成适合app展示的格式
     *
     * @param e 枚举对象
     * @return value@longname
     */
    public static String toAppEnumValue(Object e) {
        return toAppEnumValue(e, ENUM_DESC_ATTR_NAME, ENUM_SYMBOL);
    }

    /**
     * 把枚举转换成适合app展示的格式
     *
     * @param e            枚举对象
     * @param descAttrName 描述的枚举属性名
     * @return value@descAttrName
     */
    public static String toAppEnumValue(Object e, String descAttrName) {
        return toAppEnumValue(e, descAttrName, ENUM_SYMBOL);
    }

    /**
     * 把枚举转换成适合app展示的格式
     *
     * @param e            枚举对象
     * @param descAttrName 描述的枚举属性名
     * @param symbol       value和desc之间的连接符号
     * @return value+symbol+descAttrName
     */
    public static String toAppEnumValue(Object e, String descAttrName, String symbol) {
        if (CommUtil.isNull(e)) {
            return "";
        }

        if (!(e instanceof Enum)) {
            throw new BusinessException(BusiErrorCode.SYS_ERROR, "must input enum!");
        }
        fieldNotNull(descAttrName);
        descAttrName = descAttrName.substring(0, 1).toUpperCase() + descAttrName.substring(1, descAttrName.length());

        String v;
        try {
            Method valueMethod = e.getClass().getMethod("getValue");
            Method longNameMethod = e.getClass().getMethod("get" + descAttrName);
            v = valueMethod.invoke(e) + symbol + longNameMethod.invoke(e);
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException(ex);
        } catch (InvocationTargetException ex) {
            throw new RuntimeException(ex);
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        }
        return v;
    }

    public static long addMins(long millis, int amount) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(millis);
        calendar.add(Calendar.MINUTE, Math.toIntExact(amount));
        return calendar.getTimeInMillis();
    }

    public static void initTrxnEnv() {
        TrxnEnvs trxnEnvs = CommUtil.getTrxnEnvs();
        //触发交易的时间
        trxnEnvs.setComputer_date(CommUtil.getComputerDate());
        trxnEnvs.setComputer_time(CommUtil.getComputerTime());
//        trxnEnvs.setApp_token(request.getHeader(HeaderConst.TOKEN_HEADER));
        trxnEnvs.setSystem_id(systemId);
        String trxnSeq = ApSeqUtil.genSeq(ApConstants.TRXN_SEQ_CODE);
        //生成交易流水
        trxnEnvs.setTrxn_seq(trxnSeq);
        //TODO 生成批量特有的交易流水
        trxnEnvs.setBusi_seq(trxnSeq);
        CommUtil.setTrxnEnvs(trxnEnvs);
    }

    public static boolean isAmtNegative(String amt) {
        return new BigDecimal(amt).compareTo(BigDecimal.ZERO) <= 0;
    }

    public static String getPreViewUrl(String fileId, long time) {
        if (CommUtil.isNull(fileId)) {
            return "";
        }
        return FileStorageProcessorFactory.ofDownload()
                .setObjectKey(fileId)
                //可选，不设置需在配置文件设置默认值
                .setExpirationTimeInSeconds(time)
                //可选，启用多个配置时，必须指定使用哪个配置
                .generateTemporaryUrl();
    }

    public static String getPreViewUrl(String fileId) {
        return getPreViewUrl(fileId, 60L);
    }

    public static String getPublicPreViewUrl(String fileId) {
        if (CommUtil.isNull(fileId)) {
            return "";
        }
        return FileStorageProcessorFactory.ofDownload()
                .setObjectKey(fileId).generatePublicUrl();
    }

    public static boolean isValidPin(String pin) {
        String regAsc = "^(012345|123456|234567|345678|456789|)$";
        String regDesc = "^(987654|876543|765432|654321|543210|)$";
        String regSame = "(.)\\1+";
        return !(pin.matches(regAsc) || pin.matches(regDesc) || pin.matches(regSame));
    }

    public static boolean isValidPassword(String pin) {
        if (pin.length() < 8 || pin.length() > 20) {
            return false;
        }

        String regAsc = "^(01234567|12345678|23456789|012345678|123456789|0123456789|)$";
        String regDesc = "^(98765432|87654321|76543210|876543210|987654321|9876543210)$";
        String regSame = "(.)\\1+";
        return !(pin.matches(regAsc) || pin.matches(regDesc) || pin.matches(regSame));
    }

    public static boolean isValidCorporLoginPassword(String password) {
        String regex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[&!@#*%/]).{6,13}$";
        return password.matches(regex);
    }

    public static String generatePassword(String uid) {
        String LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
        String UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        String DIGITS = "0123456789";
        String SPECIAL = "&!@#*%/";
        String ALL = LOWERCASE + UPPERCASE + DIGITS + SPECIAL;

        SecureRandom random = new SecureRandom();

        StringBuilder password = new StringBuilder();
        // 确保至少有一个小写字母
        password.append(LOWERCASE.charAt(random.nextInt(LOWERCASE.length())));
        // 确保至少有一个大写字母
        password.append(UPPERCASE.charAt(random.nextInt(UPPERCASE.length())));
        // 确保至少有一个数字
        password.append(DIGITS.charAt(random.nextInt(DIGITS.length())));
        // 确保至少有一个特殊字符
        password.append(SPECIAL.charAt(random.nextInt(SPECIAL.length())));
        // 生成剩余的字符，长度在2-9之间
        int remainingLength = random.nextInt(8) + 2;
        for (int i = 0; i < remainingLength; i++) {
            password.append(ALL.charAt(random.nextInt(ALL.length())));
        }
        // 打乱密码顺序
        String shuffled = shuffleString(random, password.toString());

        log.info("generatePassword uid = " + uid + ", password = " + shuffled);

        return shuffled;
    }

    private static String shuffleString(SecureRandom random, String input) {
        char[] characters = input.toCharArray();
        for (int i = characters.length - 1; i > 0; i--) {
            int index = random.nextInt(i + 1);
            char temp = characters[index];
            characters[index] = characters[i];
            characters[i] = temp;
        }
        return new String(characters);
    }

    public static String genInviterCode(String saleId) {
        if (saleId.startsWith("SM")) {
            return "100" + saleId.substring(2);
        }
        if (saleId.startsWith("SA")) {
            return "101" + saleId.substring(2);
        }

        //个人邀请码
        return "2" + saleId.substring(1);
    }


    public static String toNGAAmountStr(BigDecimal amount) {
        DecimalFormat decimalFormat = new DecimalFormat("#,##0.00");
        String formattedAmount = decimalFormat.format(amount);
        return formattedAmount;
    }

    /**
     * 短信内邮箱，加密邮箱
     *
     * @param email
     * @return
     */
    public static String encryEmail(String email) {
        int ind = -1;
        if ((ind = email.indexOf("@")) == -1) {
            return email;
        }

        String preStr = email.substring(0, ind);
        String posStr = email.substring(ind);
        if (preStr.length() <= 5) {
            return email;
        }

        return preStr.substring(0, 2) + "***" + preStr.substring(preStr.length() - 3) + posStr;
    }

    public static String encryPhone(String phone) {
        if(CommUtil.isNull(phone) || phone.length() != 10) {
            return phone;
        }

        return phone.substring(0, 2) + "*****" + phone.substring(phone.length() - 3);
    }

    public static S2SOption buildKycHeader(String kycAppId, String kycAppSecret, String callSeqNo, String uid) {
        S2SOption s2sOption = S2SOption.builder().build();
        s2sOption.setTimeout(50);
        Map<String, String> headers = new HashMap<>();
        headers.put(HeaderConstants.REQUEST_ID_HEADER, callSeqNo);
        headers.put(HeaderConstants.APP_ID, kycAppId);
        headers.put(HeaderConstants.TOKEN_HEADER, TokenUtil.generateKycRequestToken(kycAppId + kycAppSecret + callSeqNo));
        headers.put(HeaderConstants.UID_HEADER, uid);
        headers.put(HeaderConstants.DEVICE_ID_HEADER, "");
        s2sOption.setHeaders(headers);
        return s2sOption;
    }

    public static String toBigDecimalStr(String str, int scale) {
        if (CommUtil.isNull(str)) {
            return "";
        }
        BigDecimal bd = new BigDecimal(str);
        BigDecimal result = bd.setScale(scale, BigDecimal.ROUND_HALF_UP);
        return String.valueOf(result);
    }

    public static boolean isValidNin(String nin) {
        String reg = "^\\d{11}$";
        return nin.matches(reg);
    }

    public static boolean isValidBvn(String nvn) {
        String reg = "^\\d{11}$";
        return nvn.matches(reg);
    }

    public static int dateDiff(String type, String firstDate, String secondDate) {
        Date startDate = toDate(firstDate);
        Date endDate = toDate(secondDate);
        LocalDate d1 = new LocalDate(startDate);
        LocalDate d2 = new LocalDate(endDate);
        if (!"day".equals(type) && !"dd".equals(type)) {
            if (!"year".equals(type) && !"yy".equals(type)) {
                if (!"month".equals(type) && !"mm".equals(type)) {
                    if (!"quarter".equals(type) && !"qq".equals(type)) {
                        if (!"week".equals(type) && !"ww".equals(type)) {
                            throw new IllegalArgumentException("Parameter " + type + " is invalid!");
                        } else {
                            return (Days.daysBetween(d1, d2).getDays() + 7) / 7;
                        }
                    } else {
                        return (Months.monthsBetween(d1.withDayOfMonth(1), d2.withDayOfMonth(1)).getMonths() + 3) / 3;
                    }
                } else {
                    return Months.monthsBetween(d1.withDayOfMonth(1), d2.withDayOfMonth(1)).getMonths();
                }
            } else {
                return Years
                        .yearsBetween(d1.withMonthOfYear(1).withDayOfMonth(1), d2.withMonthOfYear(1).withDayOfMonth(1))
                        .getYears();
            }
        } else {
            return Days.daysBetween(d1, d2).getDays();
        }
    }

    /**
     * 执行Linux命令
     */
    public static void execCommand(String commands) throws IOException {
        Process process = null;
        BufferedReader reader = null;
        try {
            //创建进程实例执行Linux命令
            process = Runtime.getRuntime().exec(commands);
            // 获取标准输入流
            reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
//                log.info("linux----" + line);
            }
            // 等待进程结束
            int exitCode = process.waitFor();
            if (exitCode != 0) {
                log.error("执行Linux命令异常,exitCode={},linux command={}", exitCode, commands);
                throw new IllegalArgumentException("执行Linux命令异常");

            }
        } catch (IOException | InterruptedException e) {
            log.error("执行Linux命令异常", e);
            throw new IllegalArgumentException("执行Linux命令异常");
        } finally {
            if (reader != null) {
                reader.close();
            }
            if (process != null) {
                process.destroy();
            }
        }
    }

    public static String getProdId(String acctNo) {
        log.info("getProdId acctNo: " + acctNo);

        String prodId = BaseEnumType.E_COREPRODTYPE.PCURRENT.getValue();
        if (acctNo.startsWith(ApConstants.CORPOR_ACCT_PREFIX)) {
            prodId = BaseEnumType.E_COREPRODTYPE.CCURRENT.getValue();
        } else if (acctNo.startsWith(ApConstants.CORPOR_VIRACCT_PREFIX)) {
            prodId = BaseEnumType.E_COREPRODTYPE.CVIRACCT.getValue();
        }

        return prodId;
    }

    public static Date firstDayOfMonth(Date date) {
        if (Objects.isNull(date)) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        return calendar.getTime();
    }

    /**
     * 获取当月最后一天日期
     *
     * @param date 指定时间
     * @return
     */
    public static Date lastDayOfMonth(Date date) {
        if (Objects.isNull(date)) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.add(Calendar.MONTH, 1);
        cal.add(Calendar.DATE, -1);
        return cal.getTime();
    }

    public static Date getCurrentDate() {
        return new Date();
    }

    public static String buildSqlInStr(List<String> list) {
        if (CommUtil.isNull(list)) {
            return "";
        }

        String strs = "";
        for (String str : list) {
            strs = strs + "'" + str + "',";
        }
        return strs.substring(0, strs.length() - 1);
    }

    /**
     * @param key
     * @return value key对应公共运行变量的值 公共运行变量不存在，返回null
     * @Author lid
     * <p>
     * <li>2016年12月13日-下午3:42:26</li>
     * <li>功能说明：根据key获取公共运行变量的value</li>
     * </p>
     */
    public static Object getTrxRunEnvsValue(String key) {
        if (CommUtil.isNull(key))
            return null;

        Object value = null;
        TrxnEnvs runEnvs = CommUtil.getTrxnEnvs();
        try {
            Method method = TrxnEnvs.class.getMethod("get" + key.substring(0, 1).toUpperCase() + key.substring(1));
            value = method.invoke(runEnvs);
        } catch (Exception e) {
            // 取不到值则默认为空
            log.error("Get Trx RunEnvs Value failed. [%s]", e, e.getMessage());
        }
        return value;
    }

    /**
     * @param list   待排序的List对象
     * @param isAsc  是否升序 true - 升序 false - 降序
     * @param fields 排序字段数组，可变参数
     * @Author <p>
     * <li>2014年3月4日-下午5:14:20<>
     * <li>功能说明：List多字段的正向排序。<>
     * </p>
     */
    // @BusiComponent(name="listSort",longnme="List多字段的正向排序",type=Type.tech)
    public static <E> void listSort(List<E> list, final boolean isAsc, final String... fields) {

        Collections.sort(list, new Comparator<Object>() {
            public int compare(Object a, Object b) {

                int ret = 0;
                for (String field : fields) {

                    // 获取每一个排序字段对应的值
                    Object aValue = null;
                    Object bValue = null;
                    try {
                        aValue = PropertyUtils.getProperty(a, field);
                        bValue = PropertyUtils.getProperty(b, field);
                    } catch (Exception e) {
                        log.error("Get property error:" + e.getMessage(), e, new Object[0]);
                    }
                    // 比较
                    ret = myCompare(aValue, bValue);
                    if (ret != 0) {
                        break;
                    }
                }
                if (isAsc)
                    return ret;
                else
                    return -1 * ret;

            }
        });
    }

    /**
     * @param obj1
     * @param obj2
     * @return
     * @Author T
     * <p>
     * <li>2014年6月19日-下午8:11:01</li>
     * <li>功能说明：对象比较，仅支持String, int, BigDecimal, DefaultEnum</li>
     * <li>注：该方法视空与null相等</li>
     * </p>
     */
    @SuppressWarnings("rawtypes")
    private static int myCompare(Object obj1, Object obj2) {
        if (obj1 == null && obj2 == null)
            return 0;
        if (obj2 != null && obj1 == null) {
            if ("".equals(obj2)) {
                return 0;
            }
            return -1;
        }
        if (obj2 == null) {
            if ("".equals(obj1)) {
                return 0;
            }
            return 1;
        }
        if (BigDecimal.class.isAssignableFrom(obj1.getClass()) && BigDecimal.class.isAssignableFrom(obj2.getClass())) {
            return ((BigDecimal) obj1).compareTo((BigDecimal) obj2);
        } else if (String.class.isAssignableFrom(obj1.getClass()) && String.class.isAssignableFrom(obj2.getClass())) {
            return ((String) obj1).compareTo((String) obj2);
        } else if (Integer.class.isAssignableFrom(obj1.getClass()) && Integer.class.isAssignableFrom(obj2.getClass())) {
            return (Integer) obj1 - (Integer) obj2;
        } else if (Long.class.isAssignableFrom(obj1.getClass()) && Long.class.isAssignableFrom(obj2.getClass())) {
            return (int) ((Long) obj1 - (Long) obj2);
        } else if (Enum.class.isAssignableFrom(obj1.getClass()) && Enum.class.isAssignableFrom(obj2.getClass())) {
            return myCompare(((Enum) obj1).toString(), ((Enum) obj2).toString());
        }
        throw new IllegalArgumentException("unsupported type comparison");
    }

    /**
     * 比较两个对象，如果它们都实现了Comparable接口。
     *
     * @param a 第一个对象
     * @param b 第二个对象
     * @return 比较结果
     */
    public static int compare(Object a, Object b) {
        if (a instanceof Comparable && b instanceof Comparable) {
            Comparable c1 = (Comparable) a;
            Comparable c2 = (Comparable) b;
            return c1.compareTo(c2);
        }
        throw new IllegalArgumentException("Both arguments must implement Comparable interface");
    }

    public static BigDecimal scale(BigDecimal x, int scale) {
        if (x == null) {
            return null;
        }
        return x.setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 格式化数字
     *
     * @param x 目标小数
     * @return 字符串
     */
    public static String formatDec(BigDecimal x) {
        if (x == null) {
            return null;
        }
        return new DecimalFormat("#,##,###.####").format(x);
    }

    public static ComAp.CalInstOut calFixMaturInst(String startDate, String endDate, BigDecimal calBal, BigDecimal yearRate, BigDecimal taxRate, Integer yearDays) {
        if (CommUtil.isNull(startDate)) {
            startDate = getComputerDate();
        }
        if (yearDays == null) {
            yearDays = 365;
        }
        // 总积数
        BigDecimal totlSunBal = calBal.multiply(BigDecimal.valueOf(dateDiff("day", startDate, endDate)));
        // 本期总利息 = 总积数 * 年利率 / 年计息天数 ： （这样避免先算日利率带精度，而后再乘以大数值积数使误差放大）
        BigDecimal totalInst = totlSunBal.multiply(yearRate).divide(BigDecimal.valueOf(yearDays.intValue() * 100L), 12, BigDecimal.ROUND_HALF_UP);
        BigDecimal instTax = totalInst.multiply(taxRate.divide(BigDecimal.valueOf(100)));
        ComAp.CalInstOut out = new ComAp.CalInstOut();
        out.setInterest(totalInst);
        out.setInterestTax(instTax);
        return out;
    }

    public static ComAp.CalInstOut calFixMaturInst(String startDate, String endDate, BigDecimal calBal, BigDecimal yearRate, BigDecimal taxRate) {
        return calFixMaturInst(startDate, endDate, calBal, yearRate, taxRate, 365);
    }

}
